package baseFx.serialization.json.jackson;

import baseFx.common.Dynamic;
import baseFx.common.utils.ExceptionUtils;
import baseFx.serialization.json.Json;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.module.SimpleModule;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class JacksonImpl implements Json {
    private ObjectMapper mapper;

    public JacksonImpl() {
        SimpleModule module = new SimpleModule();
        //setDeserializers必须在addXXX之前调用，否则会覆盖
        module.setDeserializers(new HookDeserializers());
        module.addSerializer(Number.class, new NumberSerializer());
        module.addSerializer(Enum.class, new EnumSerializer());
        module.setSerializerModifier(new SerializerModifier());
        module.addSerializer(Dynamic.class, new DynamicSerializer());
        module.addDeserializer(Dynamic.class, new DynamicDeserializer());
        LongDeserializer longDeserializer = new LongDeserializer();
        module.addDeserializer(Long.class, longDeserializer);
        module.addDeserializer(long.class, longDeserializer);
        mapper = new ObjectMapper();
        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
        mapper.registerModule(module);
    }

    @Override
    public String toJson(Object object) {
        try {
            return mapper.writeValueAsString(object);
        } catch (JsonProcessingException e) {
            throw ExceptionUtils.toRuntime(e);
        }
    }

    @Override
    public <T> T toObject(String json, Class<T> clazz) {
        try {
            if (clazz == String.class) {
                return (T) json;
            }
            return mapper.readValue(json, clazz);
        } catch (IOException e) {
            throw ExceptionUtils.toRuntime(e);
        }
    }

    @Override
    public <T> List<T> toList(String json, Class<T> clazz) {
        if (json == null) {
            json = "[]";
        }
        try {
            return mapper.readValue(json, mapper.getTypeFactory().constructParametricType(List.class, clazz));
        } catch (IOException e) {
            throw ExceptionUtils.toRuntime(e);
        }
    }

    @Override
    public <T> T toObject(String json, String path, Class<T> clazz) {
        try {
            JsonNode root = mapper.readTree(json);
            JsonNode node = find(root, path);
            if (node == null) {
                return null;
            }
            return mapper.treeToValue(node, clazz);
        } catch (IOException e) {
            throw ExceptionUtils.toRuntime(e);
        }
    }

    @Override
    public <T> List<T> toList(String json, String path, Class<T> clazz) {
        if (json == null) {
            json = "[]";
        }
        try {
            JsonNode root = mapper.readTree(json);
            JsonNode node = find(root, path);
            if (node == null || node.isArray() == false) {
                return null;
            }
            List<T> list = new ArrayList<>();
            for (int i = 0; i < node.size(); i++) {
                list.add(mapper.treeToValue(node.get(i), clazz));
            }
            return list;
        } catch (IOException e) {
            throw ExceptionUtils.toRuntime(e);
        }
    }

    private JsonNode find(JsonNode node, String path) {
        String[] keys = path.split("\\.");
        for (String key : keys) {
            if (key.endsWith("]")) {
                int s = key.lastIndexOf("[");
                String name = key.substring(0, s);
                if (node.has(name) == false) {
                    return null;
                }
                node = node.get(name);
                if (node.isArray() == false) {
                    return null;
                }
                int idx = Integer.parseInt(key.substring(s + 1, key.lastIndexOf("]")));
                if (idx < node.size()) {
                    node = node.get(idx);
                } else {
                    return null;
                }
            } else {
                if (node.has(key)) {
                    node = node.get(key);
                } else {
                    return null;
                }
            }
        }
        return node;
    }

    @Override
    public String raw(String json, String path) {
        try {
            JsonNode root = mapper.readTree(json);
            JsonNode node = find(root, path);
            if (node == null) {
                return null;
            }
            String value = node.toString();
            if (value.startsWith("\"") && value.endsWith("\"")) {
                return value.substring(1, value.length() - 1);
            }
            return value;
        } catch (IOException e) {
            throw ExceptionUtils.toRuntime(e);
        }
    }

//    public static void main(String[] args) {
//        String json = "{\"code\":\"0\",\"msg\":\"请求成功\",\"transactionTime\":20151022044027, \"tickets\":[{\"value\":\"ticket_string\",\"expire_in\":\"3600\",\"expire_time\":\"20151022044027\"}]}";
//        JacksonImpl impl = new JacksonImpl();
//        System.out.println(impl.raw(json, "tickets[0]"));
//        System.out.println(impl.raw(json, "transactionTime"));
//        System.out.println(impl.raw(json, "msg"));
//    }
}
